package core.units;

import geom.Coordinate;
import geom.Dimension;
import geom.Vector;

import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Dimension2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;

import core.Actor;

public class Unit extends Actor {

	private Point2D myCenter;

	private Dimension2D mySize;

	private Vector myVelocity;

	private double myHeading;

	private java.awt.Shape myRepresentation;

	// cache the expensive stuff to speed up rendering
	private Area myBounds;

	private boolean isVisible;

	private boolean hasChanged;

	public Unit(String name, String type, Point2D center, Dimension2D size, Shape graphic,
			Vector speed) {
		super(name, type);
		myRepresentation = graphic;
		setCenter(center);
		setSize(size);
		setHeading(0);
		myVelocity = speed;
		hasChanged = true;
		isVisible = true;
	}
	
	public double getLeft() {
		return getCenter().getX() - getSize().getWidth() / 2;
	}

	public double getTop() {
		return getCenter().getY() + getSize().getHeight() / 2;
	}

	public double getRight() {
		return getCenter().getX() + getSize().getWidth() / 2;
	}

	public double getBottom() {
		return getCenter().getY() - getSize().getHeight() / 2;
	}

	public Point2D getCenter() {
		return myCenter;
	}

	public Dimension2D getSize() {
		return new Dimension(mySize.getWidth(), mySize.getHeight());
	}

	public double getHeading() {
		return Math.toDegrees(myHeading);
	}

	public Vector getVelocity() {
		return myVelocity;
	}

	public boolean isVisible() {
		return isVisible;
	}

	public boolean contains(Coordinate point) {
		return getBounds().contains(point);
	}

	public void setCenter(Point2D center) {
		myCenter = new Coordinate(center.getX(), center.getY());
		hasChanged = true;
	}

	public void setSize(Dimension2D size) {
		mySize = new Dimension(size.getWidth(), size.getHeight());
		hasChanged = true;
	}

	public void setHeading(double degrees) {
		myHeading = Math.toRadians(degrees);
		hasChanged = true;
	}

	public void setVelocity(Vector v) {
		myVelocity.setDirection(v.getDirection());
		myVelocity.setSpeed(v.getSpeed());
		hasChanged = true;
	}

	public void show() {
		setVisible(true);
	}

	public void hide() {
		setVisible(false);
	}

	public void translate(double dx, double dy) {
		myCenter.setLocation(myCenter.getX() + dx, myCenter.getY() + dy);
		hasChanged = true;
	}

	public void scale(double xFactor, double yFactor) {
		mySize.setSize(mySize.getWidth() * xFactor, mySize.getHeight()
				* yFactor);
		hasChanged = true;
	}

	public void rotate(double degrees) {
		myHeading += Math.toRadians(degrees);
		hasChanged = true;
	}

	protected AffineTransform getTransform() {
		AffineTransform result = new AffineTransform();
		result.translate(myCenter.getX(), myCenter.getY());
		result.rotate(myHeading);
		result.scale(mySize.getWidth(), -mySize.getHeight());
		result.translate(-0.5, -0.5);
		return result;
	}

	protected void render(java.awt.Graphics2D pen) {
		pen.fill(myRepresentation);
	}

	protected void setVisible(boolean visibility) {
		isVisible = visibility;
	}

	protected boolean hasChanged() {
		return hasChanged;
	}

	protected Area getBounds() {
		if (hasChanged()) {
			GeneralPath path = new GeneralPath(myRepresentation);
			path.transform(getTransform());
			myBounds = new java.awt.geom.Area(path);
			hasChanged = false;
		}
		return myBounds;
	}

	public boolean intersects(Unit other) {
		java.awt.geom.Dimension2D size = getSize();
		java.awt.geom.Dimension2D otherSize = other.getSize();
		double max = Math.max(size.getWidth(), size.getHeight())
				+ Math.max(otherSize.getWidth(), otherSize.getHeight());
		if (getCenter().distance(other.getCenter()) < max) {
			java.awt.geom.Area result = (java.awt.geom.Area) getBounds()
					.clone();
			result.intersect(other.getBounds());
			return !result.isEmpty();
		}
		return false;
	}

	@Override
	public void paint(Graphics2D pen) {
		if (isVisible()) {
			// save current graphics state
			java.awt.geom.AffineTransform old = pen.getTransform();
			// correctly position shape relative to others
			pen.transform(getTransform());
			// actually show the shape
			render(pen);
			// restore graphics state
			pen.setTransform(old);
		}
	}
}
